package cn.jb698.codec.msm.generator.v5base;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.velocity.VelocityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

import cn.jb698.codec.msm.generator.util.BeanUtils;
import cn.jb698.codec.msm.generator.util.CodecCfgObjUtils;
import cn.jb698.codec.msm.generator.util.CodecGeneUtils;
import cn.jb698.codec.msm.generator.util.CodecVelocityUtils;
import cn.jb698.codec.msm.generator.v5base.beans.BcrudCfgJavaFileObj;
import cn.jb698.codec.msm.generator.v5base.beans.BcrudCfgTableObj;
import cn.jb698.codec.msm.generator.v5base.beans.BcrudConfigBean;
import cn.jb698.codec.msm.generator.v5base.beans.FileOutputTpltCfg;

/**
 * 
 * @author Administrator
 * @date 2025-03-17 19:15:50
 */
public class BcrudCreateCodeFilesImpl {

    /**
     * 日志
     */
	private static Logger log = LoggerFactory.getLogger(BcrudCreateCodeFilesImpl.class);
	
	
	   /**
     * 创建JSON文件到输出目录
     * 
     * @param cfgBean
     * @param tableJsBeans
     * @return
     * @throws IOException
     */
//    @Override
    public List<String> doCfgTableObjListOutputToJsonFiles(BcrudConfigBean cfgBean, List<BcrudCfgTableObj> tableJsBeans) throws Exception {
        List<String> filePathList = new ArrayList<>();

        if (tableJsBeans == null || tableJsBeans.size() < 1) {
            return filePathList;
        }
        String jsonFileOutDir = cfgBean.getJsonFileOutDir();
        File jsonFileOutDirFile = new File(jsonFileOutDir);
        if (!jsonFileOutDirFile.exists()) {
            jsonFileOutDirFile.mkdirs();
        }

        for (BcrudCfgTableObj tmpTableObj : tableJsBeans) {
            String jsonFileName = tmpTableObj.getTableName() + ".json";
            log.debug("生成后的json文件名为:" + jsonFileName);
            String jsonStr = JSONObject.toJSONString(tmpTableObj, true);
            String outFilePath = jsonFileOutDir + jsonFileName;
            FileWriter fwriter = new FileWriter(outFilePath);
            fwriter.write(jsonStr);
            fwriter.flush();
            fwriter.close();
            filePathList.add(outFilePath);
        }
        return filePathList;
    }
    

	/**
	 * 创建并输出代码文件.
	 * 
	 * @param configBean
	 * @param tableJsBean
	 * @throws ParseException
	 * @throws IOException
	 */
	public void doCodeFilesCreateAndOutput(BcrudConfigBean configBean, BcrudCfgTableObj tableJsBean) throws IOException, ParseException {

		String codeOutDir = configBean.getJavaCodeRootDir(); // 输出源代码的根目录

		if (codeOutDir == null) {
			throw new IllegalArgumentException("代码输出路径不能为空.");
		}
		String resOutDir = configBean.getResCodeRootDir(); // 输出资源文件的根目录
		if (resOutDir == null) {
			resOutDir = codeOutDir;
		}
		String pageOutDir = configBean.getPageCodeRootDir(); // 页面代码根路径.
		if (pageOutDir == null) {
			pageOutDir = resOutDir + "/pages/";
		}

		Map<String, Object> toContextDataMap = BeanUtils.transBeanToMap(tableJsBean, "class");
	    VelocityContext dataContext = new VelocityContext(); // 创建Context 内容
	    for (String key : toContextDataMap.keySet()) {
	        dataContext.put(key, toContextDataMap.get(key));
	    }

		// 计算更新表达式
		Map<String, BcrudCfgJavaFileObj> fileObjMap = tableJsBean.getFileObjMap();
		if(Objects.nonNull(fileObjMap) && !fileObjMap.isEmpty()) {
		    VelocityContext dataContextStream = dataContext;
		    List<BcrudCfgJavaFileObj> toCalcFileObjList = new ArrayList<>();
		    Set<Entry<String, BcrudCfgJavaFileObj>> entrySet = fileObjMap.entrySet();
		    for (Entry<String, BcrudCfgJavaFileObj> entry : entrySet) {
//		        String key = entry.getKey();
		        BcrudCfgJavaFileObj value = entry.getValue();
		        toCalcFileObjList.add(value);
            }
		    if(!toCalcFileObjList.isEmpty()) {
		        // 排序
		        toCalcFileObjList = toCalcFileObjList.stream().sorted((cmp1,cmp2)-> {return Integer.compare(cmp1.getOrder(), cmp2.getOrder());}).collect(Collectors.toList());
		        // 按顺序执行
		        toCalcFileObjList.stream().forEach(obj->{
		            List<String> importList = obj.getImportList();
		            String packageName = obj.getPackageName();
//		            String objFullName = obj.getObjFullName();
		            boolean hasEval = false;
		            if(packageName.indexOf("{") >= 0 ) {
		                packageName = CodecVelocityUtils.evaluateToStr(packageName, dataContextStream);
		                hasEval = true;
		            }
                    if(hasEval) {
                        obj.setPackageName(packageName);
                        obj.doInitProp();
                    }
		            if(Objects.nonNull(importList) && !importList.isEmpty() ) {
		                hasEval = false;
		                List<String> newImportList = new ArrayList<>(importList.size());
		                for (String tmpImport : importList) {
		                    if(tmpImport.indexOf("{") >= 0 ) {
		                        tmpImport = CodecVelocityUtils.evaluateToStr(tmpImport, dataContextStream);
		                        hasEval = true;
		                    }
		                    newImportList.add(tmpImport);
		                }
		                if(hasEval) {
		                    obj.setImportList(newImportList);
		                    obj.setHasImportListSorted(true);
		                }
		            }

		            if(!obj.isHasImportListSorted()) {
		                List<String> newImportList = CodecCfgObjUtils.doCalcImportList(obj.getImportList(), tableJsBean);
                        obj.setImportList(newImportList);
		            }
		        });
		    }
		}

		// 初始化的脚本
		String initTemplateStr = null; // 模板内容
		// //////////////////////////////////////////////////////////////////////////////////////////////////////

		// 其它层代码输出(默认有数据则输出)
		List<FileOutputTpltCfg> fileOutputTpltCfgList = configBean.getFileOutputTpltCfgList();

		if (fileOutputTpltCfgList != null && fileOutputTpltCfgList.size() > 0) {

	        // 模板数据初始化
	        toContextDataMap = BeanUtils.transBeanToMap(tableJsBean, "class");
	        dataContext = new VelocityContext(); // 创建Context 内容
	        for (String key : toContextDataMap.keySet()) {
	            dataContext.put(key, toContextDataMap.get(key));
	        }

            VelocityContext dataContextStream = dataContext;
	        // 输出模板Express处理
	        fileOutputTpltCfgList.stream().forEach(obj->{
	            String outputFileRelDir = obj.getOutputFileRelDir();
	            String outputFileName = obj.getOutputFileName();
	            {
	                boolean hasEval = false;
	                if(outputFileName.indexOf("{") > 0 ) {
	                    outputFileName = CodecVelocityUtils.evaluateToStr(outputFileName, dataContextStream);
	                    hasEval = true;
	                }
	                if(hasEval) {
	                    obj.setOutputFileName(outputFileName);
	                }
	            }
	            {
	                boolean hasEval = false;
	                if(outputFileRelDir.indexOf("{") > 0 ) {
	                    outputFileRelDir = CodecVelocityUtils.evaluateToStr(outputFileRelDir, dataContextStream);
	                    hasEval = true;
	                }
	                if(hasEval) {
	                    obj.setOutputFileRelDir(outputFileRelDir);
	                }
	            }
	        });
			doCodeFilesParseAndOutput(fileOutputTpltCfgList, configBean, toContextDataMap, dataContext, initTemplateStr);
		}
	}

	/**
	 * 解析并输出文件
	 * 
	 * @param fileOutputTpltCfgList 输出文件配置列表
	 * @param configBean 配置Bean 
	 * @param dataMap 数据Map
	 * @param context Velocity数据上下文
	 * @param initTemplateStr 初始化的模板内容
	 * @throws IOException 
	 * @throws ParseException
	 */
	private void doCodeFilesParseAndOutput(List<FileOutputTpltCfg> fileOutputTpltCfgList, BcrudConfigBean configBean, Map<String, Object> dataMap, VelocityContext context,
	        String initTemplateStr) throws IOException, ParseException {
		Writer writer;
		String outFilePath;
		for (FileOutputTpltCfg tmpCfg : fileOutputTpltCfgList) {
			String tpltFileRes = tmpCfg.getTpltFileRes();
			String outputFileDir = tmpCfg.getOutputFileRelDir();
			String outputFileName = tmpCfg.getOutputFileName();

			// 计算绝对路径目录及文件名称
			String parse_outputFileDir = CodecVelocityUtils.evaluateToStr(outputFileDir, dataMap);
			String tgt_outputFileDir = CodecGeneUtils.parseAndCombineDirStr(configBean.getProjRootDir(), parse_outputFileDir);

			String parse_outputFileName = CodecVelocityUtils.evaluateToStr(outputFileName, dataMap);
			String tgt_fileName = parse_outputFileName;

			InputStream tpltFileIs = this.getClass().getResourceAsStream(tpltFileRes); // 模板文件输入流
			String tgt_fileTpltCtt = null; // 最终的 模板 内容
			if (tpltFileIs != null) {
				tgt_fileTpltCtt = CodecVelocityUtils.parseVelocityTemplate(null, tpltFileIs);
				if(Objects.nonNull(initTemplateStr)) {
				    tgt_fileTpltCtt = initTemplateStr + tgt_fileTpltCtt; // 补充初始化脚本
				}
			}
			if (tgt_fileTpltCtt != null) {
				try {
					outFilePath = tgt_outputFileDir + tgt_fileName;
					log.debug("输出文件:" + outFilePath);
					writer = new FileWriter(outFilePath);
					CodecVelocityUtils.getDefaultVe().evaluate(context, writer, "", tgt_fileTpltCtt);
					writer.flush();
				} catch (Exception e) {
					log.error("解析出现异常了,对应的输出模板内容:\n" + tgt_fileTpltCtt );
					throw new RuntimeException(e);
				}
			}else {
				log.error("输出文件配置为空,(tpltFileRes="+tpltFileRes+")"); 
			}
		}
	}



}
